雖然是倒數三天~
但是沒有囤貨還是怕怕
驗證模式跟授權模式
到Maven搜尋spring boot starter security
選擇3.0.1版本
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.0.1</version>
</dependency>
按”網站動起來”
pom.xml程式碼
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.tzu</groupId>
<artifactId>myweb</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>myweb</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>3.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
<version>3.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>3.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>3.0.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
到瀏覽器看http://localhost:8080/login
你正在使用 Spring Boot 3.0.2 以及其他相關的 Spring Boot 套件和依賴項。此 pom.xml 檔案看起來完好無損。
修改ApplictionConfig 程式碼加入@EnableWebSecurity
package com.tzu.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import com.mysql.cj.jdbc.MysqlDataSource;
import com.tzu.beans.HelloBean;
import com.tzu.beans.HelloProxy;
import com.tzu.beans.IHello;
import com.tzu.beans.TWHello;
import com.tzu.domain.ApiKeyRepository;
import com.tzu.filter.ApiKeyFilter;
//透過方法生產Bean物件 註冊到Spring容器去
@EnableWebSecurity
@Configuration
public class ApplictionConfig {
//Attribute 使用spEL ${}
//@Value標註取出預設組態application.properties設定項目
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.password}")
private String password;
public ApplictionConfig() {
System.out.println("Configuration Bean配置了");
}
//生產一個HelloBean物件
@Bean(name="hellonean")
public HelloBean getHelloBean() {
System.out.println("Hello Bean產生了");
//建構HelloBean
HelloBean hello=new HelloBean();
return hello;
}
@Bean
public TWHello getTWHello() {
System.out.println("TW Hello Bean產生了");
//建構HelloBean
TWHello hello=new TWHello();
return hello;
}
//參數使用定義Bean alias Name 注入依賴 隨著窗口物件注入到對方去 進行反轉物件注入
@Bean
public HelloProxy getHelloProxy(TWHello bean) {
var helloProxy=new HelloProxy(bean);
return helloProxy;
}
//產生一個DataSource 是共用的物件(連接物件工廠 整個應用系統工廠只要一個即可)
@Bean
@Scope("singleton")
public DataSource createDataSource() {
System.out.println("Datasource:"+this.url);
//建構MySQLDataSource
MysqlDataSource datasource=new MysqlDataSource();
//配置要件 URL/User name/password
datasource.setUrl(url);
datasource.setUser(userName);
datasource.setPassword(password);
//Driver 會進行內部使用
return datasource;
}
//生產JdbcTemplate元件(Spring Bean)
//透過IoC注入控制反轉 注入DataSource物件
@Bean //生命週期每一個注入 產生一個體
public JdbcTemplate createJdbcTemplate(DataSource datasource) {
//建構JdbcTemplate物件
System.out.println("JdbcTemplate 注入的DataSource:"+datasource.toString());
JdbcTemplate template=new JdbcTemplate();
template.setDataSource(datasource); //Property Injection屬性注入依賴物件
return template;
}
//註冊Filter bean 進入Spring Container
//使用IoC 注入控制反轉 注入需要JpaRepository
@Bean
public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository reposit) {
System.out.println("註冊Filter攔截器 ApiKeyFilter..."+reposit);
FilterRegistrationBean<ApiKeyFilter> register=
new FilterRegistrationBean<>();
var filter=new ApiKeyFilter();
filter.setReposit(reposit); //透過Property Injection
register.setFilter(filter); //註冊Filter
//設定url pattern
register.addUrlPatterns("/api/customers/*","/api/hello/*"); //設定Filter UrlPattern 攔截起來的端點
return register;
}
}
Spring Boot ApplictionConfig
類別已經被修改以加入 Spring Security 和其他功能。在配置中,@EnableWebSecurity
註解表明正在啟用 Web 安全性。此外, apikeyFilter
方法中還注入了一個 ApiKeyRepository
,並設置給 ApiFilter
物件。
這些都是正確的變更,應用程序需要安全性和一個 API 金鑰驗證過濾器,這些變更是合理的。
修改ApplictionConfig 程式碼加入
package com.tzu.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import com.mysql.cj.jdbc.MysqlDataSource;
import com.tzu.beans.HelloBean;
import com.tzu.beans.HelloProxy;
import com.tzu.beans.IHello;
import com.tzu.beans.TWHello;
import com.tzu.domain.ApiKeyRepository;
import com.tzu.filter.ApiKeyFilter;
//透過方法生產Bean物件 註冊到Spring容器去
@EnableWebSecurity
@Configuration
public class ApplictionConfig {
//Attribute 使用spEL ${}
//@Value標註取出預設組態application.properties設定項目
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.password}")
private String password;
public ApplictionConfig() {
System.out.println("Configuration Bean配置了");
}
//生產一個HelloBean物件
@Bean(name="hellonean")
public HelloBean getHelloBean() {
System.out.println("Hello Bean產生了");
//建構HelloBean
HelloBean hello=new HelloBean();
return hello;
}
@Bean
public TWHello getTWHello() {
System.out.println("TW Hello Bean產生了");
//建構HelloBean
TWHello hello=new TWHello();
return hello;
}
//參數使用定義Bean alias Name 注入依賴 隨著窗口物件注入到對方去 進行反轉物件注入
@Bean
public HelloProxy getHelloProxy(TWHello bean) {
var helloProxy=new HelloProxy(bean);
return helloProxy;
}
//產生一個DataSource 是共用的物件(連接物件工廠 整個應用系統工廠只要一個即可)
@Bean
@Scope("singleton")
public DataSource createDataSource() {
System.out.println("Datasource:"+this.url);
//建構MySQLDataSource
MysqlDataSource datasource=new MysqlDataSource();
//配置要件 URL/User name/password
datasource.setUrl(url);
datasource.setUser(userName);
datasource.setPassword(password);
//Driver 會進行內部使用
return datasource;
}
//生產JdbcTemplate元件(Spring Bean)
//透過IoC注入控制反轉 注入DataSource物件
@Bean //生命週期每一個注入 產生一個體
public JdbcTemplate createJdbcTemplate(DataSource datasource) {
//建構JdbcTemplate物件
System.out.println("JdbcTemplate 注入的DataSource:"+datasource.toString());
JdbcTemplate template=new JdbcTemplate();
template.setDataSource(datasource); //Property Injection屬性注入依賴物件
return template;
}
//註冊Filter bean 進入Spring Container
//使用IoC 注入控制反轉 注入需要JpaRepository
@Bean
public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository reposit) {
System.out.println("註冊Filter攔截器 ApiKeyFilter..."+reposit);
FilterRegistrationBean<ApiKeyFilter> register=
new FilterRegistrationBean<>();
var filter=new ApiKeyFilter();
filter.setReposit(reposit); //透過Property Injection
register.setFilter(filter); //註冊Filter
//設定url pattern
register.addUrlPatterns("/api/customers/*","/api/hello/*"); //設定Filter UrlPattern 攔截起來的端點
return register;
}
//產生Web Security Config
//使用注入控制反轉 (IoC)
@Bean
public SecurityFilterChain configSecurity(HttpSecurity http) {
System.out.println(http.toString());
return null;
}
}
Spring Boot ApplictionConfig
類別似乎繼續進行修改以支援 Spring Security。你現在已經有了 SecurityFilterChain
方法,這是一個 Spring Security 配置的一部分,它允許你定義安全性規則,例如誰可以訪問哪些 URL。
在 configSecurity
方法中,你可以配置相關的 Spring Security 設定。這將涉及到 URL 的權限設定、使用者身份驗證等。你可以使用 http
參數來設定這些安全性規則。
顯示
再修改ApplictionConfig 程式碼加入CSRF安全性
package com.tzu.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import com.mysql.cj.jdbc.MysqlDataSource;
import com.tzu.beans.HelloBean;
import com.tzu.beans.HelloProxy;
import com.tzu.beans.IHello;
import com.tzu.beans.TWHello;
import com.tzu.domain.ApiKeyRepository;
import com.tzu.filter.ApiKeyFilter;
//透過方法生產Bean物件 註冊到Spring容器去
@EnableWebSecurity
@Configuration
public class ApplictionConfig {
//Attribute 使用spEL ${}
//@Value標註取出預設組態application.properties設定項目
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.password}")
private String password;
public ApplictionConfig() {
System.out.println("Configuration Bean配置了");
}
//生產一個HelloBean物件
@Bean(name="hellonean")
public HelloBean getHelloBean() {
System.out.println("Hello Bean產生了");
//建構HelloBean
HelloBean hello=new HelloBean();
return hello;
}
@Bean
public TWHello getTWHello() {
System.out.println("TW Hello Bean產生了");
//建構HelloBean
TWHello hello=new TWHello();
return hello;
}
//參數使用定義Bean alias Name 注入依賴 隨著窗口物件注入到對方去 進行反轉物件注入
@Bean
public HelloProxy getHelloProxy(TWHello bean) {
var helloProxy=new HelloProxy(bean);
return helloProxy;
}
//產生一個DataSource 是共用的物件(連接物件工廠 整個應用系統工廠只要一個即可)
@Bean
@Scope("singleton")
public DataSource createDataSource() {
System.out.println("Datasource:"+this.url);
//建構MySQLDataSource
MysqlDataSource datasource=new MysqlDataSource();
//配置要件 URL/User name/password
datasource.setUrl(url);
datasource.setUser(userName);
datasource.setPassword(password);
//Driver 會進行內部使用
return datasource;
}
//生產JdbcTemplate元件(Spring Bean)
//透過IoC注入控制反轉 注入DataSource物件
@Bean //生命週期每一個注入 產生一個體
public JdbcTemplate createJdbcTemplate(DataSource datasource) {
//建構JdbcTemplate物件
System.out.println("JdbcTemplate 注入的DataSource:"+datasource.toString());
JdbcTemplate template=new JdbcTemplate();
template.setDataSource(datasource); //Property Injection屬性注入依賴物件
return template;
}
//註冊Filter bean 進入Spring Container
//使用IoC 注入控制反轉 注入需要JpaRepository
@Bean
public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository reposit) {
System.out.println("註冊Filter攔截器 ApiKeyFilter..."+reposit);
FilterRegistrationBean<ApiKeyFilter> register=
new FilterRegistrationBean<>();
var filter=new ApiKeyFilter();
filter.setReposit(reposit); //透過Property Injection
register.setFilter(filter); //註冊Filter
//設定url pattern
register.addUrlPatterns("/api/customers/*","/api/hello/*"); //設定Filter UrlPattern 攔截起來的端點
return register;
}
//產生Web Security Config
//使用注入控制反轉 (IoC)
@Bean
public SecurityFilterChain configSecurity(HttpSecurity http) {
System.out.println(http.toString());
try {
http.csrf()
.and();
http.formLogin().loginPage("/mylogin");
return http.build();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
Spring Boot ApplictionConfig
類別看起來已經具備了 Spring Security 的配置。在 configSecurity
方法中,已經定義了一些 Spring Security 設定,例如 CSRF 保護和自訂登入頁面。
然而,在的 configSecurity
方法中,目前處於捕捉異常的狀態,因為 http.build()
可能會拋出例外。在這裡,需要返回一個 SecurityFilterChain
物件,而不是 http
物件。
這裡是一個簡單的例子,設定 Spring Security 的基本設定,包括 CSRF 保護和自訂登入頁面:
@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers("/mylogin").permitAll() // 自訂登入頁面允許所有人訪問
.anyRequest().authenticated()
)
.formLogin(withDefaults());
return http.build();
}
這個 FormController
類別是一個 Spring MVC 控制器,用於處理登入頁面的請求。根據你的程式碼,它處理GET請求 /mylogin
並返回名為 "login" 的視圖。這意味著它將尋找名為 "login" 的視圖模板,以便在網頁上顯示登入表單。
請確保專案中有一個名為 "login.html" 或 "login.jsp" 或任何你設定的視圖名稱,它是真實存在的視圖模板。此模板應該包含登入表單的HTML代碼,以便用戶輸入他們的憑據。
新增檔案FormController.java
package com.tzu.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class FormController {
@GetMapping("/mylogin")
public String login() {
return "login";
}
}
你的 FormController
控制器處理GET請求 /mylogin
,然後返回名為 "login" 的視圖名稱。這意味著當用戶訪問 /mylogin
時,將呈現名為 "login" 的視圖模板。
新增檔案login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登入頁面</title>
</head>
<body>
<fieldset>
<legend>登入作業</legend>
</fieldset>
</body>
</html>
你的 "login.html" 似乎只包含了一個基本的HTML骨架,但缺少實際的登入表單內容。為了讓用戶能夠輸入他們的登入資訊,你應該在 "fieldset" 元素中添加一個表單。以下是一個簡單的示例,展示如何添加一個包含用戶名和密碼輸入字段以及登入按鈕的表單:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登入頁面</title>
</head>
<body>
<fieldset>
<legend>登入作業</legend>
<form action="/login" method="post">
<label for="username">使用者名稱:</label>
<input type="text" id="username" name="username" required><br><br>
<label for="password">密碼:</label>
<input type="password" id="password" name="password" required><br><br>
<button type="submit">登入</button>
</form>
</fieldset>
</body>
</html>
這個示例包含一個表單,用戶可以輸入他們的使用者名稱和密碼,然後按 "登入" 按鈕提交表單。你可以根據你的需求自定義表單內容。
請確保將這個HTML保存為 "login.html" 並放在你的視圖文件夾中,以便Spring Boot應用程式可以正確找到它。當用戶訪問 "/mylogin" 時,他們將看到這個登入表單。
瀏覽器測試http://localhost:8080/
後端顯示
再修改ApplictionConfig.java
package com.tzu.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import com.mysql.cj.jdbc.MysqlDataSource;
import com.tzu.beans.HelloBean;
import com.tzu.beans.HelloProxy;
import com.tzu.beans.IHello;
import com.tzu.beans.TWHello;
import com.tzu.domain.ApiKeyRepository;
import com.tzu.filter.ApiKeyFilter;
//透過方法生產Bean物件 註冊到Spring容器去
@EnableWebSecurity
@Configuration
public class ApplictionConfig {
//Attribute 使用spEL ${}
//@Value標註取出預設組態application.properties設定項目
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.password}")
private String password;
public ApplictionConfig() {
System.out.println("Configuration Bean配置了");
}
//生產一個HelloBean物件
@Bean(name="hellonean")
public HelloBean getHelloBean() {
System.out.println("Hello Bean產生了");
//建構HelloBean
HelloBean hello=new HelloBean();
return hello;
}
@Bean
public TWHello getTWHello() {
System.out.println("TW Hello Bean產生了");
//建構HelloBean
TWHello hello=new TWHello();
return hello;
}
//參數使用定義Bean alias Name 注入依賴 隨著窗口物件注入到對方去 進行反轉物件注入
@Bean
public HelloProxy getHelloProxy(TWHello bean) {
var helloProxy=new HelloProxy(bean);
return helloProxy;
}
//產生一個DataSource 是共用的物件(連接物件工廠 整個應用系統工廠只要一個即可)
@Bean
@Scope("singleton")
public DataSource createDataSource() {
System.out.println("Datasource:"+this.url);
//建構MySQLDataSource
MysqlDataSource datasource=new MysqlDataSource();
//配置要件 URL/User name/password
datasource.setUrl(url);
datasource.setUser(userName);
datasource.setPassword(password);
//Driver 會進行內部使用
return datasource;
}
//生產JdbcTemplate元件(Spring Bean)
//透過IoC注入控制反轉 注入DataSource物件
@Bean //生命週期每一個注入 產生一個體
public JdbcTemplate createJdbcTemplate(DataSource datasource) {
//建構JdbcTemplate物件
System.out.println("JdbcTemplate 注入的DataSource:"+datasource.toString());
JdbcTemplate template=new JdbcTemplate();
template.setDataSource(datasource); //Property Injection屬性注入依賴物件
return template;
}
//註冊Filter bean 進入Spring Container
//使用IoC 注入控制反轉 注入需要JpaRepository
@Bean
public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository reposit) {
System.out.println("註冊Filter攔截器 ApiKeyFilter..."+reposit);
FilterRegistrationBean<ApiKeyFilter> register=
new FilterRegistrationBean<>();
var filter=new ApiKeyFilter();
filter.setReposit(reposit); //透過Property Injection
register.setFilter(filter); //註冊Filter
//設定url pattern
register.addUrlPatterns("/api/customers/*","/api/hello/*"); //設定Filter UrlPattern 攔截起來的端點
return register;
}
//產生Web Security Config
//使用注入控制反轉 (IoC)
@Bean
public SecurityFilterChain configSecurity(HttpSecurity http) {
System.out.println(http.toString());
try {
http.csrf()
.and();
http.formLogin().loginPage("/mylogin")
.and()
.securityMatcher("/**")
.authorizeHttpRequests()
.requestMatchers("/","/mylogin").permitAll() //允許匿名存取
.anyRequest()
.authenticated(); //調用到登入頁面 登入頁面並沒有匿名存取 又要求到自己一次....
return http.build();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
你的 Spring Security 配置看起來幾乎正確,不過有一些小的錯誤。這是修正後的配置:
// ...
@EnableWebSecurity
@Configuration
public class ApplicationConfig extends SecurityFilterChain {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.password}")
private String password;
@Autowired
private ApiKeyRepository apiKeyRepository;
@Bean
public DataSource createDataSource() {
MysqlDataSource datasource = new MysqlDataSource();
datasource.setUrl(url);
datasource.setUser(userName);
datasource.setPassword(password);
return datasource;
}
@Bean
public JdbcTemplate createJdbcTemplate(DataSource datasource) {
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(datasource);
return template;
}
@Bean
public FilterRegistrationBean<ApiKeyFilter> apiKeyFilter() {
FilterRegistrationBean<ApiKeyFilter> register = new FilterRegistrationBean<>();
ApiKeyFilter filter = new ApiKeyFilter();
filter.setApiKeyRepository(apiKeyRepository);
register.setFilter(filter);
register.addUrlPatterns("/api/customers/*", "/api/hello/*");
return register;
}
@Override
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.formLogin()
.loginPage("/mylogin")
.and()
.authorizeRequests()
.antMatchers("/", "/mylogin").permitAll()
.anyRequest().authenticated();
return http.build();
}
}
這是修正後的版本,主要更改如下:
@EnableWebSecurity
放在 SecurityFilterChain
擴展的配置類上。SecurityFilterChain
擴展中,透過 securityFilterChain
方法來定義安全過濾器鏈。try...catch
區塊,不再需要捕獲 Exception
。請確保將這個配置保存到正確的文件中,並確保你的 SecurityFilterChain
擴展名為 ApplicationConfig
。這樣應該會正確配置 Spring Security,允許匿名訪問首頁和登入頁面,並要求對其他頁面進行身份驗證。
瀏覽器測試http://localhost:8080/mylogin
http://localhost:8080/login
按ENTER自動帶入http://localhost:8080/mylogin
修改login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登入頁面</title>
</head>
<body>
<fieldset>
<legend>登入作業</legend>
<form method="post">
<div>使用者帳號</div>
<input type="text" name="userName"/>
<div>密碼</div>
<input type="password" name="password"/>
<input type="submit" value="登入"/>
</form>
</fieldset>
</body>
</html>
你的登入頁面看起來不錯,提供了使用者名稱和密碼的輸入框以及登入按鈕。不過,目前你的表單僅包含前端部分,你需要將其連線到後端 Spring Security 配置中。
你可以通過以下步驟將其與 Spring Security 配置連線:
建立登入表單的提交路徑(通常為 /login
)。
更新 Spring Security 配置以指示登入頁面和成功後的重定向。
下面是如何做的:
首先,為登入表單建立一個提交路徑。你可以在 mylogin.html
的 <form>
標籤中新增 action
屬性來指定登入提交的 URL,如下所示:
<form method="post" action="/login">
接下來,確保你的 Spring Security 配置檔案 ApplicationConfig
包括以下更改:
@Override
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.formLogin()
.loginPage("/mylogin") // 登入頁面的 URL
.loginProcessingUrl("/login") // 提交登入表單的 URL
.defaultSuccessURL("/dashboard", true) // 登入成功後的重定向 URL
.and()
.authorizeRequests()
.antMatchers("/", "/mylogin").permitAll()
.anyRequest().authenticated();
return http.build();
}
上述更改中,我們做了以下幾個關鍵的更改:
loginPage
方法指定登入頁面的 URL。loginProcessingUrl
方法指定登入表單提交的 URL。defaultSuccessURL
方法指定登入成功後的重定向 URL,此處假設你有一個名為 /dashboard
的成功後的頁面。你可以根據你的需求更改這個 URL。通過這些更改,你的登入表單將與 Spring Security 連線,使用者在提交表單後將被重定向到指定的成功後的頁面。請確保在實際應用中替換示例中的 URL。
修改FormController
package com.tzu.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class FormController {
@GetMapping("/mylogin")
public String login() {
return "login";
}
//進行安全性驗證
@PostMapping("/uservalid")
public String userValid(String userName,String password) {
System.out.println("登入帳號:"+userName);
//進行使用者驗證
//安全性設定(Identity/Roles/Principal/ Context...
return "ok";
}
}
FormController
看起來已經包括了登入頁面 (/mylogin
) 的 GET
請求和使用者驗證 (/uservalid
) 的 POST
請求。然而,你可能需要對 /uservalid
請求進行更多的處理,包括實際的使用者驗證和Spring Security的使用。
下面是關於如何處理 /uservalid
請求的進一步建議:
實際使用者驗證:在 /uservalid
請求處理方法中,你需要實際驗證使用者提供的使用者名稱和密碼。你可以使用 Spring Security 或自定義的驗證邏輯來完成這一步驟。通常,你需要檢查提供的使用者名稱和密碼是否與資料庫中的使用者憑據匹配。
Spring Security 整合:如果你使用 Spring Security,你可以讓 Spring Security 來處理使用者驗證,而不是在 /uservalid
請求中自行驗證。Spring Security提供了一種強大的方式來管理使用者、角色和許可權。你可以配置 Spring Security 來指定如何進行身份驗證、授權等。
錯誤處理:如果驗證失敗,你應該考慮如何處理錯誤情況。通常,你會顯示錯誤訊息,或者重定向到登入頁面並顯示錯誤訊息。
在你的 FormController
中,你可以像下面這樣處理使用者驗證:
@PostMapping("/uservalid")
public String userValid(String userName, String password) {
// 假設這裡有用於驗證使用者的邏輯
if (isUserValid(userName, password)) {
// 驗證成功,重定向到成功頁面
return "redirect:/dashboard";
} else {
// 驗證失敗,重定向到登入頁面並顯示錯誤訊息
return "redirect:/mylogin?error=true";
}
}
這個示例中,isUserValid(userName, password)
是用於實際驗證使用者的自定義方法。如果驗證失敗,它將重定向到登入頁面並附帶錯誤參數,以便在登入頁面上顯示錯誤訊息。
請確保實際的使用者驗證邏輯與你的應用需求和 Spring Security 的配置一致。
再給驗證過樣板OK
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登入成功</title>
</head>
<body>
</body>
</html>
你已經建立了一個名為 "login-success.html" 的登入成功頁面。這個頁面看起來很簡單,但可以根據需要進一步擴充套件以提供更多的資訊或功能。
如果你希望在登入成功後顯示這個頁面,可以在使用者驗證成功的條件下,返回該頁面的檢視名稱。在 Spring MVC 中,你可以使用 @Controller
控制器中的 @PostMapping
方法來實現這一點。
以下是如何返回 "login-success.html" 檢視的示例程式碼:
@Controller
public class FormController {
// ...
@PostMapping("/uservalid")
public String userValid(String userName, String password) {
// 假設這裡有用於驗證使用者的邏輯
if (isUserValid(userName, password)) {
// 驗證成功,返回登入成功頁面的檢視名稱
return "login-success";
} else {
// 驗證失敗,重定向到登入頁面並顯示錯誤訊息
return "redirect:/mylogin?error=true";
}
}
}
在這個示例中,如果使用者驗證成功,就返回 "login-success" 檢視名稱,這將對應到 "login-success.html" 頁面。
確保 "login-success.html" 檔案位於你的 Spring Boot 項目的檢視資料夾中,通常是 "resources/templates"。這樣 Spring Boot 將能夠找到並渲染這個檢視。
瀏覽器http://localhost:8080/customers/qry/country會自動指向http://localhost:8080/mylogin
再修改ApplictionConfig讓靜態css跟ui都可以通過
package com.tzu.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import com.mysql.cj.jdbc.MysqlDataSource;
import com.tzu.beans.HelloBean;
import com.tzu.beans.HelloProxy;
import com.tzu.beans.IHello;
import com.tzu.beans.TWHello;
import com.tzu.domain.ApiKeyRepository;
import com.tzu.filter.ApiKeyFilter;
//透過方法生產Bean物件 註冊到Spring容器去
@EnableWebSecurity
@Configuration
public class ApplictionConfig {
//Attribute 使用spEL ${}
//@Value標註取出預設組態application.properties設定項目
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.password}")
private String password;
public ApplictionConfig() {
System.out.println("Configuration Bean配置了");
}
//生產一個HelloBean物件
@Bean(name="hellonean")
public HelloBean getHelloBean() {
System.out.println("Hello Bean產生了");
//建構HelloBean
HelloBean hello=new HelloBean();
return hello;
}
@Bean
public TWHello getTWHello() {
System.out.println("TW Hello Bean產生了");
//建構HelloBean
TWHello hello=new TWHello();
return hello;
}
//參數使用定義Bean alias Name 注入依賴 隨著窗口物件注入到對方去 進行反轉物件注入
@Bean
public HelloProxy getHelloProxy(TWHello bean) {
var helloProxy=new HelloProxy(bean);
return helloProxy;
}
//產生一個DataSource 是共用的物件(連接物件工廠 整個應用系統工廠只要一個即可)
@Bean
@Scope("singleton")
public DataSource createDataSource() {
System.out.println("Datasource:"+this.url);
//建構MySQLDataSource
MysqlDataSource datasource=new MysqlDataSource();
//配置要件 URL/User name/password
datasource.setUrl(url);
datasource.setUser(userName);
datasource.setPassword(password);
//Driver 會進行內部使用
return datasource;
}
//生產JdbcTemplate元件(Spring Bean)
//透過IoC注入控制反轉 注入DataSource物件
@Bean //生命週期每一個注入 產生一個體
public JdbcTemplate createJdbcTemplate(DataSource datasource) {
//建構JdbcTemplate物件
System.out.println("JdbcTemplate 注入的DataSource:"+datasource.toString());
JdbcTemplate template=new JdbcTemplate();
template.setDataSource(datasource); //Property Injection屬性注入依賴物件
return template;
}
//註冊Filter bean 進入Spring Container
//使用IoC 注入控制反轉 注入需要JpaRepository
@Bean
public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository reposit) {
System.out.println("註冊Filter攔截器 ApiKeyFilter..."+reposit);
FilterRegistrationBean<ApiKeyFilter> register=
new FilterRegistrationBean<>();
var filter=new ApiKeyFilter();
filter.setReposit(reposit); //透過Property Injection
register.setFilter(filter); //註冊Filter
//設定url pattern
register.addUrlPatterns("/api/customers/*","/api/hello/*"); //設定Filter UrlPattern 攔截起來的端點
return register;
}
//產生Web Security Config
//使用注入控制反轉 (IoC)
@Bean
public SecurityFilterChain configSecurity(HttpSecurity http) {
System.out.println(http.toString());
SecurityFilterChain chain=null;
try {
http.csrf()
.and();
http.formLogin().loginPage("/mylogin")
.and()
.authorizeHttpRequests()
.requestMatchers("/","/mylogin","/uservalid","/customers/**").permitAll() //允許匿名存取
.requestMatchers("/css/**","/html/**","/js/**","/ui/**").permitAll(); //針對static file允續匿名存取
//.anyRequest()
//.authenticated(); //調用到登入頁面 登入頁面並沒有匿名存取 又要求到自己一次....
chain=http.build();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return chain;
}
}
你的 Spring Security 配置看起來不錯,但還有一些小問題需要解決:
configSecurity
方法中,你使用了 http.formLogin().loginPage("/mylogin")
來設定登入頁面。這是正確的,但你還需要配置登入表單的 POST 請求。這將在使用者提交登入表單時觸發使用者驗證。你需要新增以下配置:http.formLogin()
.loginPage("/mylogin")
.loginProcessingUrl("/uservalid") // 配置處理登入的 URL
.defaultSuccessURL("/login-success", true) // 登入成功後的預設 URL
.permitAll();
這將設定登入頁面為 "/mylogin",並配置登入請求提交到 "/uservalid" 來處理使用者驗證。還配置了登入成功後的預設 URL 為 "/login-success"。
/login-success
URL 新增一個對映。建立一個控制器方法,類似於之前的 /mylogin
控制器方法,用於顯示登入成功的頁面。@Controller
public class FormController {
// ...
@GetMapping("/login-success")
public String loginSuccess() {
return "login-success";
}
}
如果你希望匿名使用者能夠訪問靜態檔案(如 CSS、HTML、JS 等),可以在 configSecurity
方法中使用 .permitAll()
配置。在你的程式碼中已經包含了類似的配置。確保你的靜態檔案位於配置的靜態資料夾下。
請確保 login-success.html
頁面存在於 "resources/templates" 目錄下,這樣 Spring Boot 將能夠找到並渲染它。
最後,檢查你的使用者驗證邏輯是否正確,以便根據使用者名稱和密碼驗證使用者。
這些調整應該能夠使你的使用者登入和驗證功能正常工作。
瀏覽器測試:http://localhost:8080/customers/allcustomers
按編輯也顯示
再修改FormController
package com.tzu.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class FormController {
@RequestMapping(path="/mylogin",method= {RequestMethod.GET,RequestMethod.POST})
public String login(String userName,String password) {
//第一次請求
if(userName==null) {
return "login";
}else
{
System.out.println(userName);
//進行登入驗證
return "login";
}
}
//進行安全性驗證
@RequestMapping(path="/mylogin/uservalid",method= {RequestMethod.POST,RequestMethod.GET})
public String userValid(String userName,String password) {
System.out.println("登入帳號:"+userName);
//進行使用者驗證
//安全性設定(Identity/Roles/Principal/ Context...
return "ok";
}
}
你的控制器程式碼有一些改進的空間:
@GetMapping
和 @PostMapping
註解來提高程式碼的可讀性。這將減少在請求對映上的冗餘配置。以下是一種更簡化的方式:@Controller
public class FormController {
@GetMapping("/mylogin")
public String showLoginForm() {
return "login";
}
@PostMapping("/mylogin")
public String login(String userName, String password) {
if (userName != null) {
System.out.println(userName);
// 進行登入驗證
return "login";
}
return "login";
}
@PostMapping("/mylogin/uservalid")
public String userValid(String userName, String password) {
System.out.println("登入帳號:" + userName);
// 進行使用者驗證
// 安全性設定 (Identity/Roles/Principal/Context...
return "ok";
}
}
注意 /mylogin/uservalid
的對映路徑中沒有必要包含 /mylogin
,因為你可以在不同方法中使用相同的路徑,Spring 會根據請求方法來區分。
在登入方法中,你應該驗證使用者名稱和密碼,而不是隻檢查使用者名稱是否為 null。實際上,這個方法應該是驗證使用者身份的地方,如果驗證失敗,你可以根據需要返回錯誤頁面或重定向。
確保你的登入頁面(login.html
)以及成功登入後的頁面(ok.html
)存在於 "resources/templates" 目錄下。這樣 Spring Boot 才能正確找到它們。
這些改進將幫助你的程式碼更清晰和易於維護。如果你已經在 userValid
方法中實現了使用者驗證邏輯,那麼登入和驗證部分應該可以正常工作。確保根據驗證結果返回適當的檢視,以實現登入成功或失敗的使用者體驗。
再修改ApplictionConfig
package com.tzu.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import com.mysql.cj.jdbc.MysqlDataSource;
import com.tzu.beans.HelloBean;
import com.tzu.beans.HelloProxy;
import com.tzu.beans.IHello;
import com.tzu.beans.TWHello;
import com.tzu.domain.ApiKeyRepository;
import com.tzu.filter.ApiKeyFilter;
//透過方法生產Bean物件 註冊到Spring容器去
@EnableWebSecurity
@Configuration
public class ApplictionConfig {
//Attribute 使用spEL ${}
//@Value標註取出預設組態application.properties設定項目
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.password}")
private String password;
public ApplictionConfig() {
System.out.println("Configuration Bean配置了");
}
//生產一個HelloBean物件
@Bean(name="hellonean")
public HelloBean getHelloBean() {
System.out.println("Hello Bean產生了");
//建構HelloBean
HelloBean hello=new HelloBean();
return hello;
}
@Bean
public TWHello getTWHello() {
System.out.println("TW Hello Bean產生了");
//建構HelloBean
TWHello hello=new TWHello();
return hello;
}
//參數使用定義Bean alias Name 注入依賴 隨著窗口物件注入到對方去 進行反轉物件注入
@Bean
public HelloProxy getHelloProxy(TWHello bean) {
var helloProxy=new HelloProxy(bean);
return helloProxy;
}
//產生一個DataSource 是共用的物件(連接物件工廠 整個應用系統工廠只要一個即可)
@Bean
@Scope("singleton")
public DataSource createDataSource() {
System.out.println("Datasource:"+this.url);
//建構MySQLDataSource
MysqlDataSource datasource=new MysqlDataSource();
//配置要件 URL/User name/password
datasource.setUrl(url);
datasource.setUser(userName);
datasource.setPassword(password);
//Driver 會進行內部使用
return datasource;
}
//生產JdbcTemplate元件(Spring Bean)
//透過IoC注入控制反轉 注入DataSource物件
@Bean //生命週期每一個注入 產生一個體
public JdbcTemplate createJdbcTemplate(DataSource datasource) {
//建構JdbcTemplate物件
System.out.println("JdbcTemplate 注入的DataSource:"+datasource.toString());
JdbcTemplate template=new JdbcTemplate();
template.setDataSource(datasource); //Property Injection屬性注入依賴物件
return template;
}
//註冊Filter bean 進入Spring Container
//使用IoC 注入控制反轉 注入需要JpaRepository
@Bean
public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository reposit) {
System.out.println("註冊Filter攔截器 ApiKeyFilter..."+reposit);
FilterRegistrationBean<ApiKeyFilter> register=
new FilterRegistrationBean<>();
var filter=new ApiKeyFilter();
filter.setReposit(reposit); //透過Property Injection
register.setFilter(filter); //註冊Filter
//設定url pattern
register.addUrlPatterns("/api/customers/*","/api/hello/*"); //設定Filter UrlPattern 攔截起來的端點
return register;
}
//產生Web Security Config
//使用注入控制反轉 (IoC)
@Bean
public SecurityFilterChain configSecurity(HttpSecurity http) {
System.out.println(http.toString());
//設定CSRF 安全性 產生403 status
SecurityFilterChain chain=null;
try {
http.csrf()
.and();
http.formLogin().loginPage("/mylogin")
.and()
.securityMatcher("/**")
.authorizeHttpRequests()
.requestMatchers("/","/mylogin").permitAll() //允許匿名存取
.requestMatchers("/css/**","/html/**","/js/**","/ui/**").permitAll(); //針對static file允續匿名存取
//.anyRequest()
//.authenticated(); //調用到登入頁面 登入頁面並沒有匿名存取 又要求到自己一次....
//... 匿名存取網站 通通可以通行
//透過HttpSecurity Builder(工廠)生產出SecurityFilterChain
chain=http.build();
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
}
return chain;
}
}
你的 Spring Boot 配置類(ApplictionConfig
)已經配置了一些基本的安全性設定,但還可以進行一些改進:
使用 permitAll()
允許訪問靜態檔案的配置是正確的,但可以更精簡。你可以將這個配置移動到 Spring Security 的 WebSecurity
配置中,而不是 HttpSecurity
配置中。
securityMatcher("/**")
指定了所有請求都需要進行許可權驗證,這包括靜態資源,但你已經允許匿名訪問靜態資源。這裡的配置可以更清晰,只需要指定需要認證的路徑。
針對 loginPage("/mylogin")
,你可以在 Spring Security 配置中提供登入頁的路徑,但需要確保你的登入頁面路徑正確,與控制器中的路徑匹配。
以下是修改後的程式碼示例:
@EnableWebSecurity
@Configuration
public class ApplictionConfig {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String userName;
@Value("${spring.datasource.password}")
private String password;
@Bean(name="hellonean")
public HelloBean getHelloBean() {
// ...
}
@Bean
public TWHello getTWHello() {
// ...
}
@Bean
public HelloProxy getHelloProxy(TWHello bean) {
// ...
}
@Bean
public DataSource createDataSource() {
// ...
}
@Bean
public JdbcTemplate createJdbcTemplate(DataSource datasource) {
// ...
}
@Bean
public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository reposit) {
// ...
}
@Bean
public SecurityFilterChain configSecurity(HttpSecurity http) {
try {
http.csrf().disable() // 禁用 CSRF
.authorizeRequests()
.antMatchers("/", "/mylogin", "/css/**", "/html/**", "/js/**", "/ui/**").permitAll() // 允許匿名訪問
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/mylogin")
.and();
return http.build();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return null;
}
// 針對靜態資源的訪問許可權
@Configuration
public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/html/**", "/js/**", "/ui/**");
}
}
}
這種方式將靜態資源的訪問許可權配置移到了獨立的 WebSecurityConfig
類中,使程式碼更加清晰。這樣,你可以維護應用的安全性配置,並且僅需要關注主要的安全配置。
目前只有http://localhost:8080/mylogin跟http://localhost:8080開放
謝謝收看